package com.zktravel.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.google.common.collect.Lists;
import com.odianyun.util.BeanUtils;
import com.odianyun.util.exception.ExceptionUtils;
import com.odianyun.util.reflect.ReflectUtils;

@SuppressWarnings("unchecked")
public abstract class OmsBeanUtils {
	
	public static List<String> getNonNullFields(Object obj, String...excludeFields) {
		String[] getters = ReflectUtils.getterNames(obj.getClass());
		
		return getNonNullFields(obj, getters, excludeFields);
	}
	
	public static List<String> getNonNullFields(Object obj, String[] fields, String...excludeFields) {
		List<String> nonNullFields = Lists.newArrayList();
		
		List<String> excludes = Collections.EMPTY_LIST;
		if (excludeFields != null) {
			excludes = Arrays.asList(excludeFields);
		}
		for (String getter : fields) {
			if (excludes.contains(getter)) {
				continue;
			}
			Object value = ReflectUtils.callGetMethod(obj, getter);
			if (value != null) {
				nonNullFields.add(getter);
			}
		}
		return nonNullFields;
	}
	
	/**
	 * 复制source实例的字段到target实例，同时排除source的父类，父类的父类（假如有）一直到祖先类的字段，除了exceptParentFields指定的字段，其他父类字段都会被排除
	 * @param source
	 * @param target
	 * @param exceptParentFields 指定不排除的父类字段，即这些字段不会被排除
	 */
	public static void copyPropertiesIgnoreParents(Object source, Object target, String... exceptParentFields) {
		String[] fieldNames = getIgnoreParentFields(source.getClass().getSuperclass(), exceptParentFields);
		BeanUtils.copyProperties(source, target, fieldNames);
	}
	/**
	 * 同{@link copyPropertiesIgnoreParents(Object, Object, String...)}
	 * 自动创建targetClass的实例，要求targetClass有空参构造方法
	 * @param source
	 * @param targetClass
	 * @param exceptParentFields
	 * @return
	 */
	public static <T> T copyPropertiesIgnoreParents(Object source, Class<T> targetClass, String... exceptParentFields) {
		String[] fieldNames = getIgnoreParentFields(source.getClass().getSuperclass(), exceptParentFields);
		return BeanUtils.copyProperties(source, targetClass, fieldNames);
	}
	/**
	 * 复制一批source到一批targetClass类型的实例
	 * 同{@link copyPropertiesIgnoreParents(Object, Object, String...)}
	 * 自动创建targetClass的实例，要求targetClass有空参构造方法
	 * @param sourceList
	 * @param targetClass
	 * @param exceptParentFields
	 * @return
	 */
	public static <T> List<T> copyListIgnoreParents(List<?> sourceList, Class<T> targetClass, String... exceptParentFields) {
		if (sourceList.isEmpty()) {
			return (List<T>) Collections.EMPTY_LIST;
		}
		String[] fieldNames = getIgnoreParentFields(sourceList.get(0).getClass().getSuperclass(), exceptParentFields);
		return BeanUtils.copyList(sourceList, targetClass, fieldNames);
	}
	
	private static String[] getIgnoreParentFields(Class<?> superClass, String... exceptParentFields) {
		Field[] fields = BeanUtils.getFields(superClass.getSuperclass(), exceptParentFields);
		String[] fieldNames = new String[fields.length];
		int idx = 0;
		for (Field field : fields) {
			fieldNames[idx ++]  = field.getName();
		}
		return fieldNames;
	}

	/**
	 * 使用序列化的方式深拷贝
	 * @param src
	 * @param <T>
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public static <T> List<T> copy(List<T> src) throws IOException, ClassNotFoundException {
		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
		ObjectOutputStream out = new ObjectOutputStream(byteOut);
		out. writeObject(src);
		ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
		ObjectInputStream in = new ObjectInputStream(byteIn);
		List<T> dest = (List<T>) in.readObject();
		return dest;
	}
	
	public static Object clone(Object obj) {
		try {
			ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(byteOut);
			out. writeObject(obj);
			ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
			ObjectInputStream in = new ObjectInputStream(byteIn);
			return in.readObject();
		} catch (Exception e) {
			throw ExceptionUtils.wrap2Runtime(e);
		}
	}
}
